/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
mxCodecRegistry.register(function () {
  /**
	 * Class: mxChildChangeCodec
	 *
	 * Codec for <mxChildChange>s. This class is created and registered
	 * dynamically at load time and used implicitely via <mxCodec> and
	 * the <mxCodecRegistry>.
	 *
	 * Transient Fields:
	 *
	 * - model
	 * - previous
	 * - previousIndex
	 * - child
	 *
	 * Reference Fields:
	 *
	 * - parent
	 */
  var codec = new mxObjectCodec(new mxChildChange(),
    ['model', 'child', 'previousIndex'],
    ['parent', 'previous'])

  /**
	 * Function: isReference
	 *
	 * Returns true for the child attribute if the child
	 * cell had a previous parent or if we're reading the
	 * child as an attribute rather than a child node, in
	 * which case it's always a reference.
	 */
  codec.isReference = function (obj, attr, value, isWrite) {
    if (attr == 'child' && (!isWrite || obj.model.contains(obj.previous))) {
      return true
    }

    return mxUtils.indexOf(this.idrefs, attr) >= 0
  }

  /**
	 * Function: isExcluded
	 *
	 * Excludes references to parent or previous if not in the model.
	 */
  	codec.isExcluded = function (obj, attr, value, write) {
  		return mxObjectCodec.prototype.isExcluded.apply(this, arguments) ||
  			(write && value != null && (attr == 'previous' ||
  			attr == 'parent') && !obj.model.contains(value))
  	}

  /**
	 * Function: afterEncode
	 *
	 * Encodes the child recusively and adds the result
	 * to the given node.
	 */
  codec.afterEncode = function (enc, obj, node) {
    if (this.isReference(obj, 'child', obj.child, true)) {
      // Encodes as reference (id)
      node.setAttribute('child', enc.getId(obj.child))
    } else {
      // At this point, the encoder is no longer able to know which cells
      // are new, so we have to encode the complete cell hierarchy and
      // ignore the ones that are already there at decoding time. Note:
      // This can only be resolved by moving the notify event into the
      // execute of the edit.
      enc.encodeCell(obj.child, node)
    }

    return node
  }

  /**
	 * Function: beforeDecode
	 *
	 * Decodes the any child nodes as using the respective
	 * codec from the registry.
	 */
  codec.beforeDecode = function (dec, node, obj) {
    if (node.firstChild != null &&
			node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) {
      // Makes sure the original node isn't modified
      node = node.cloneNode(true)

      var tmp = node.firstChild
      obj.child = dec.decodeCell(tmp, false)

      var tmp2 = tmp.nextSibling
      tmp.parentNode.removeChild(tmp)
      tmp = tmp2

      while (tmp != null) {
        tmp2 = tmp.nextSibling

        if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT) {
          // Ignores all existing cells because those do not need to
          // be re-inserted into the model. Since the encoded version
          // of these cells contains the new parent, this would leave
          // to an inconsistent state on the model (ie. a parent
          // change without a call to parentForCellChanged).
          var id = tmp.getAttribute('id')

          if (dec.lookup(id) == null) {
            dec.decodeCell(tmp)
          }
        }

        tmp.parentNode.removeChild(tmp)
        tmp = tmp2
      }
    } else {
      var childRef = node.getAttribute('child')
      obj.child = dec.getObject(childRef)
    }

    return node
  }

  /**
	 * Function: afterDecode
	 *
	 * Restores object state in the child change.
	 */
  codec.afterDecode = function (dec, node, obj) {
    // Cells are decoded here after a complete transaction so the previous
    // parent must be restored on the cell for the case where the cell was
    // added. This is needed for the local model to identify the cell as a
    // new cell and register the ID.
    if (obj.child != null) {
      if (obj.child.parent != null && obj.previous != null &&
                obj.child.parent != obj.previous) {
        obj.previous = obj.child.parent
      }

      obj.child.parent = obj.previous
      obj.previous = obj.parent
      obj.previousIndex = obj.index
    }

    return obj
  }

  // Returns the codec into the registry
  return codec
}())
